#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <strings.h>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> //函数htons的作用是将主机字节序转换为网络字节序（大端字节序）
#include <functional>

// using func_t = std::function<std::string(const std::string&)>;//C++11提供的写法
typedef std::function<std::string(const std::string &)> func_t; // 函数对象 函数返回值为string类型 函数参数为const string&
#define SIZE 1024
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR
};
uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";


class UdpServer // UDP服务器类
{
public:
    void AddUser(const std::string &ip, uint16_t port, const sockaddr_in &client)
    {
        std::unordered_map<std::string, sockaddr_in>::iterator it = _online_user.find(ip);
        if (it == _online_user.end())
        {
            _online_user.insert({ip, client});
            std::cout << "sucess add [" << ip << "]" << "[" << port << "]" << std::endl;
        }
    }
    void BroadCast(const std::string &info, const std::string &ip, uint16_t port) // 消息中包含用户的ip和port以区别那位用户发送的消息
    {
        std::string message = "[";
        message = ip + "]";
        message += "[";
        message += std::to_string(port) + ":";
        message += info;
        for (const auto &user : _online_user)
        {
            socklen_t len = sizeof(user.second);
            sendto(_sockfd, message.c_str(), message.size(), 0, (const sockaddr *)(&user.first), len);
        }
    }

public:
    UdpServer(const uint16_t port = defaultport, const std::string &ip = defaultip, bool isrunning = false)
        : _port(port),
          _ip(ip),
          _isrunning(isrunning)
    {
    }
    void Init()
    {
        // 创建udp  socket
        int _sockfd = socket(AF_INET, SOCK_DGRAM, 0); // AF_INET(PF_INET)是IPv4的地址类型
        // bind socket
        struct sockaddr_in local;                   // IPv4地址用sockaddr_in结构体表示包括：16位地址类型、16位端口号、42位IP地址
        struct sockaddr *addr = (sockaddr *)&local; // c++中struct可以省略

        bzero(&local, sizeof(local)); // 将结构体内存单元清零
        // memset(&local,0,sizeof(local));

        local.sin_family = AF_INET;
        local.sin_port = htons(_port); // htons函数保证服务端主机的端口号是网络字节序，要发给其他主机的应用程序

        local.sin_addr.s_addr = inet_addr(_ip.c_str()); // inet_addr函数 string->uint32_t 并且转为网络字节序
        // local.sin_addr.s_addr=htonl(INADDR_ANY);//使用这个写法在构造函数中默认构造成员变量_ip = 0.0.0.0

        if (bind(_sockfd, addr, sizeof(addr)) < 0)
        {
            std::cout << "bind error, errno" << errno << strerror(errno) << std::endl;
            exit(BIND_ERROR);
        }
    }
    void Run(func_t func) // 降低网络通信时与处理字符串的耦合度，对代码分层
    {
        _isrunning = true;
        char inbuffer[SIZE];
        while (_isrunning)
        {
            // 从套接字获取数据
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, /*src addr*/ (sockaddr *)&client, &len);
            if (n < 0)
            {
                std::cout << "recvfrom error, errno" << errno << strerror(errno) << std::endl;
                continue;
            }
            inbuffer[n]=0;//客户端用户发来的消息
            std::cout<<"client says:"<<inbuffer<<std::endl;
            
            std::string clientip=inet_ntoa(client.sin_addr);
            uint16_t clientport = ntohs(client.sin_port);

            // 数据处理
            std::string info = func(inbuffer);
            AddUser(clientip,clientport,client);// 向_online_user中加入访问服务端的客户
            BroadCast(info,clientip,clientport);//向服务器中已保存的客户发消息
        }
    }
    ~UdpServer()
    {
        if (_sockfd > 0)
        {
            close(_sockfd);
        }
    }

private:
    int _sockfd;    // 网络文件描述符
    uint16_t _port; // 服务器进程的端口号
    std::string _ip;
    bool _isrunning;
    std::unordered_map<std::string, struct sockaddr_in> _online_user;
};